Micron Document
C.S.Burner 🪙 ━►🔥GIT Node


Displaying Raw • Download

meshchatx/src/frontend/components/interfaces/Interface.vue 9ec2a88817819d433c5d68dbec0ca070ca989b91 (9ec2a888) Text, 15.79 KB

T8b949e<!-- SPDX-License-Identifier: 0BSD AND MIT -->

Tff7b72<templateTff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffinterface-card min-w-0 transition-all duration-300Ta5d6ff"
Te6edf3:class=Ta5d6ff"Ta5d6ff{
Ta5d6ff'Ta5d6ffopacity-60 grayscale-[0.5]Ta5d6ff'Ta5d6ff: !isInterfaceEnabled(iface) || iface._restart_required || !isReticulumRunning,
}Ta5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-col sm:flex-row gap-4 sm:items-start relative pt-11 sm:pt-0Ta5d6ff"Tff7b72>
T8b949e<!-- Offline Overlay -->
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ff!isReticulumRunningTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffabsolute inset-0 z-10 flex items-center justify-center bg-white/20 dark:bg-zinc-900/20 backdrop-blur-[0.5px] rounded-3xl pointer-events-noneTa5d6ff"
Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffbg-red-500/90 text-white px-3 py-1.5 rounded-full shadow-lg flex items-center gap-2 text-[10px] font-bold uppercase tracking-widerTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6fflan-disconnectTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffw-3.5 h-3.5Ta5d6ff" Tff7b72/>
Tff7b72<spanTff7b72>Reticulum OfflineTff7b72</span>
Tff7b72</div>
Tff7b72</div>

T8b949e<!-- Restart Required Overlay -->
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffisReticulumRunning && iface._restart_requiredTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffabsolute inset-0 z-10 flex items-center justify-center bg-white/20 dark:bg-zinc-900/20 backdrop-blur-[0.5px] rounded-3xl pointer-events-noneTa5d6ff"
Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffbg-amber-500/90 text-white px-3 py-1.5 rounded-full shadow-lg flex items-center gap-2 text-[10px] font-bold uppercase tracking-widerTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffrestartTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffw-3.5 h-3.5Ta5d6ff" Tff7b72/>
Tff7b72<spanTff7b72>{{ Te6edf3$t("interfaces.restart_required") }}Tff7b72</span>
Tff7b72</div>
Tff7b72</div>

Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex gap-4 min-w-0 flex-1 sm:flex-initialTa5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffinterface-card__icon shrink-0Ta5d6ff"Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3:icon-name=Ta5d6ff"Ta5d6fficonNameTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffw-6 h-6Ta5d6ff" Tff7b72/>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex-1 min-w-0 space-y-2 overflow-hiddenTa5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-2 flex-wrapTa5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-lg font-semibold text-gray-900 dark:text-white truncate min-w-0Ta5d6ff"Tff7b72>
{{ iface._name }}
Tff7b72</div>
Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6fftype-chip shrink-0Ta5d6ff"Tff7b72>{{ iface.type }}Tff7b72</span>
Tff7b72<span Te6edf3:class=Ta5d6ff"Ta5d6ffstatusChipClassTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffshrink-0Ta5d6ff"Tff7b72>{{
isInterfaceEnabled(iface) ? Te6edf3$t("app.enabled") : Te6edf3$t("app.disabled")
}}Tff7b72</span>
Tff7b72<span Te6edf3v-if=Ta5d6ff"Ta5d6ffisDiscoverable()Ta5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffdiscoverable-chip shrink-0Ta5d6ff"Tff7b72>DiscoverableTff7b72</span>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-sm text-gray-600 dark:text-gray-300 break-words min-w-0Ta5d6ff"Tff7b72>
{{ description }}
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-wrap gap-2 text-xs text-gray-600 dark:text-gray-300Ta5d6ff"Tff7b72>
Tff7b72<span Te6edf3v-if=Ta5d6ff"Ta5d6ffiface._stats?.bitrateTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffstat-chipTa5d6ff"
Tff7b72>{{ Te6edf3$t("interface.bitrate") }} {{ formatBitsPerSecond(iface._stats?.bitrate ?? 0) }}Tff7b72</span
>
Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6ffstat-chipTa5d6ff"
Tff7b72>{{ Te6edf3$t("interface.tx") }} {{ formatBytes(iface._stats?.txb ?? 0) }}Tff7b72</span
>
Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6ffstat-chipTa5d6ff"
Tff7b72>{{ Te6edf3$t("interface.rx") }} {{ formatBytes(iface._stats?.rxb ?? 0) }}Tff7b72</span
>
Tff7b72<span Te6edf3v-if=Ta5d6ff"Ta5d6ffiface.type === Ta5d6ff'Ta5d6ffRNodeInterfaceTa5d6ff'Ta5d6ff && iface._stats?.noise_floorTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffstat-chipTa5d6ff"
Tff7b72>{{ Te6edf3$t("interface.noise") }} {{ iface._stats?.noise_floor }} dBmTff7b72</span
>
Tff7b72<span Te6edf3v-if=Ta5d6ff"Ta5d6ffiface._stats?.clients != nullTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffstat-chipTa5d6ff"
Tff7b72>{{ Te6edf3$t("interface.clients") }} {{ iface._stats?.clients }}Tff7b72</span
>
Tff7b72</div>
Tff7b72<div Te6edf3v-if=Ta5d6ff"Ta5d6ffiface._stats?.ifac_signatureTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffifac-lineTa5d6ff"Tff7b72>
Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6fftext-emerald-500 font-semiboldTa5d6ff"Tff7b72>{{ iface._stats.ifac_size * 8 }}-bit IFACTff7b72</span>
Tff7b72<span Te6edf3v-if=Ta5d6ff"Ta5d6ffiface._stats?.ifac_netnameTa5d6ff"Tff7b72>• {{ iface._stats.ifac_netname }}Tff7b72</span>
Tff7b72<spanTff7b72>•Tff7b72</span>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6fftext-blue-500 hover:underlineTa5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffonIFACSignatureClick(iface._stats.ifac_signature)Ta5d6ff"
Tff7b72>
{{ iface._stats.ifac_signature.slice(0, 8) }}…{{ iface._stats.ifac_signature.slice(-8) }}
Tff7b72</button>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffabsolute top-2 right-2 z-20 flex flex-row items-center gap-1 sm:static sm:z-auto sm:flex sm:flex-row sm:gap-2 sm:items-center sm:shrink-0 sm:justify-endTa5d6ff"
Tff7b72>
Tff7b72<button
Te6edf3v-if=Ta5d6ff"Ta5d6ffisInterfaceEnabled(iface)Ta5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinline-flex items-center justify-center rounded-full p-2 min-w-[44px] min-h-[44px] sm:min-w-0 sm:min-h-0 shrink-0 border-0 bg-transparent text-gray-600 dark:text-zinc-300 hover:bg-gray-100 dark:hover:bg-zinc-800 transition-colorsTa5d6ff"
Te6edf3:title=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffinterface.disableTa5d6ff'Ta5d6ff)Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffdisableInterfaceTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffpowerTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffw-5 h-5 sm:w-4 sm:h-4Ta5d6ff" Tff7b72/>
Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6ffhidden sm:inline sm:ml-1.5 text-xs font-semiboldTa5d6ff"Tff7b72>{{ Te6edf3$t("interface.disable") }}Tff7b72</span>
Tff7b72</button>
Tff7b72<button
v-else
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinline-flex items-center justify-center rounded-full p-2 min-w-[44px] min-h-[44px] sm:min-w-0 sm:min-h-0 shrink-0 border-0 bg-transparent text-green-600 dark:text-green-400 hover:bg-gray-100 dark:hover:bg-zinc-800 transition-colorsTa5d6ff"
Te6edf3:title=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffinterface.enableTa5d6ff'Ta5d6ff)Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffenableInterfaceTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffpowerTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffw-5 h-5 sm:w-4 sm:h-4Ta5d6ff" Tff7b72/>
Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6ffhidden sm:inline sm:ml-1.5 text-xs font-semiboldTa5d6ff"Tff7b72>{{ Te6edf3$t("interface.enable") }}Tff7b72</span>
Tff7b72</button>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffrelative z-50 shrink-0Ta5d6ff"Tff7b72>
Tff7b72<DropDownMenuTff7b72>
Tff7b72<template #buttonTff7b72>
Tff7b72<IconButtonTff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffdots-verticalTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffw-5 h-5Ta5d6ff" Tff7b72/>
Tff7b72</IconButton>
Tff7b72</template>
Tff7b72<template #itemsTff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffmax-h-60 overflow-auto py-1 space-y-1Ta5d6ff"Tff7b72>
Tff7b72<DropDownMenuItem @Te6edf3click=Ta5d6ff"Ta5d6ffeditInterfaceTa5d6ff"Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffpencilTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffw-5 h-5Ta5d6ff" Tff7b72/>
Tff7b72<spanTff7b72>{{ Te6edf3$t("interface.edit_interface") }}Tff7b72</span>
Tff7b72</DropDownMenuItem>
Tff7b72<DropDownMenuItem @Te6edf3click=Ta5d6ff"Ta5d6ffexportInterfaceTa5d6ff"Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffexportTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffw-5 h-5Ta5d6ff" Tff7b72/>
Tff7b72<spanTff7b72>{{ Te6edf3$t("interface.export_interface") }}Tff7b72</span>
Tff7b72</DropDownMenuItem>
Tff7b72<DropDownMenuItem @Te6edf3click=Ta5d6ff"Ta5d6ffdeleteInterfaceTa5d6ff"Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6fftrash-canTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffw-5 h-5 text-red-500Ta5d6ff" Tff7b72/>
Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6fftext-red-500Ta5d6ff"Tff7b72>{{ Te6edf3$t("interface.delete_interface") }}Tff7b72</span>
Tff7b72</DropDownMenuItem>
Tff7b72</div>
Tff7b72</template>
Tff7b72</DropDownMenu>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>

Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ff[Ta5d6ff'Ta5d6ffUDPInterfaceTa5d6ff'Ta5d6ff, Ta5d6ff'Ta5d6ffRNodeInterfaceTa5d6ff'Ta5d6ff].includes(iface.type)Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffmt-4 grid gap-2 text-sm text-gray-700 dark:text-gray-300Ta5d6ff"
Tff7b72>
Tff7b72<div Te6edf3v-if=Ta5d6ff"Ta5d6ffiface.type === Ta5d6ff'Ta5d6ffUDPInterfaceTa5d6ff'Ta5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffdetail-gridTa5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffdetail-labelTa5d6ff"Tff7b72>{{ Te6edf3$t("interface.listen") }}Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffdetail-value min-w-0 break-allTa5d6ff"Tff7b72>{{ iface.listen_ip }}:{{ iface.listen_port }}Tff7b72</div>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffdetail-labelTa5d6ff"Tff7b72>{{ Te6edf3$t("interface.forward") }}Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffdetail-value min-w-0 break-allTa5d6ff"Tff7b72>{{ iface.forward_ip }}:{{ iface.forward_port }}Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3v-else-if=Ta5d6ff"Ta5d6ffiface.type === Ta5d6ff'Ta5d6ffRNodeInterfaceTa5d6ff'Ta5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffdetail-gridTa5d6ff"Tff7b72>
Tff7b72<divTff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffdetail-labelTa5d6ff"Tff7b72>{{ Te6edf3$t("interface.port") }}Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffdetail-value min-w-0 break-allTa5d6ff"Tff7b72>{{ iface.port }}Tff7b72</div>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffdetail-labelTa5d6ff"Tff7b72>{{ Te6edf3$t("interface.frequency") }}Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffdetail-value min-w-0 break-allTa5d6ff"Tff7b72>{{ formatFrequency(iface.frequency) }}Tff7b72</div>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffdetail-labelTa5d6ff"Tff7b72>{{ Te6edf3$t("interface.bandwidth") }}Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffdetail-value min-w-0 break-allTa5d6ff"Tff7b72>{{ formatFrequency(iface.bandwidth) }}Tff7b72</div>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffdetail-labelTa5d6ff"Tff7b72>{{ Te6edf3$t("interface.spreading_factor") }}Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffdetail-value min-w-0 break-allTa5d6ff"Tff7b72>{{ iface.spreadingfactor }}Tff7b72</div>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffdetail-labelTa5d6ff"Tff7b72>{{ Te6edf3$t("interface.coding_rate") }}Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffdetail-value min-w-0 break-allTa5d6ff"Tff7b72>{{ iface.codingrate }}Tff7b72</div>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffdetail-labelTa5d6ff"Tff7b72>{{ Te6edf3$t("interface.txpower") }}Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffdetail-value min-w-0 break-allTa5d6ff"Tff7b72>{{ iface.txpower }} dBmTff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</template>

Tff7b72<scriptTff7b72>
import DialogUtils from "../../js/DialogUtils";
import Utils from "../../js/Utils";
import DropDownMenuItem from "../DropDownMenuItem.vue";
import IconButton from "../IconButton.vue";
import DropDownMenu from "../DropDownMenu.vue";
import MaterialDesignIcon from "../MaterialDesignIcon.vue";

export default {
name: "Interface",
components: {
DropDownMenu,
IconButton,
DropDownMenuItem,
MaterialDesignIcon,
},
props: {
iface: {
type: Object,
required: true,
},
isReticulumRunning: {
type: Boolean,
default: true,
},
},
emits: ["enable", "disable", "edit", "export", "delete"],
data() {
return {};
},
computed: {
iconName() {
switch (this.iface.type) {
case "AutoInterface":
return "home-automation";
case "RNodeInterface":
return this.iface.port && this.iface.port.startsWith("tcp://") ? "lan-connect" : "radio-tower";
case "RNodeMultiInterface":
return "access-point-network";
case "TCPClientInterface":
return "lan-connect";
case "TCPServerInterface":
return "lan";
case "UDPInterface":
return "wan";
case "SerialInterface":
return "usb-port";
case "KISSInterface":
case "AX25KISSInterface":
return "antenna";
case "I2PInterface":
return "eye";
case "PipeInterface":
return "pipe";
default:
return "server-network";
}
},
description() {
if (this.iface.type === "TCPClientInterface") {
return `${this.iface.target_host}:${this.iface.target_port}`;
}
if (this.iface.type === "TCPServerInterface" || this.iface.type === "UDPInterface") {
return `${this.iface.listen_ip}:${this.iface.listen_port}`;
}
if (this.iface.type === "SerialInterface") {
return `${this.iface.port} @ ${this.iface.speed || "9600"}bps`;
}
if (this.iface.type === "RNodeInterface" && this.iface.port && this.iface.port.startsWith("tcp://")) {
return `RNode over IP @ ${this.iface.port.replace("tcp://", "")}`;
}
if (this.iface.type === "AutoInterface") {
return "Auto-detect Ethernet and Wi-Fi peers";
}
return this.iface.description || "Custom interface";
},
statusChipClass() {
return this.isInterfaceEnabled(this.iface)
? "inline-flex items-center rounded-full bg-green-100 text-green-700 px-2 py-0.5 text-xs font-semibold"
: "inline-flex items-center rounded-full bg-red-100 text-red-700 px-2 py-0.5 text-xs font-semibold";
},
},
methods: {
onIFACSignatureClick: function (ifacSignature) {
DialogUtils.alert(ifacSignature);
},
isDiscoverable() {
const value = this.iface.discoverable;
if (typeof value === "string") {
return ["true", "yes", "1", "on"].includes(value.toLowerCase());
}
return Boolean(value);
},
isInterfaceEnabled: function (iface) {
return Utils.isInterfaceEnabled(iface);
},
enableInterface() {
this.$emit("enable");
},
disableInterface() {
this.$emit("disable");
},
editInterface() {
this.$emit("edit");
},
exportInterface() {
this.$emit("export");
},
deleteInterface() {
this.$emit("delete");
},
formatBitsPerSecond: function (bits) {
return Utils.formatBitsPerSecond(bits);
},
formatBytes: function (bytes) {
return Utils.formatBytes(bytes);
},
formatFrequency(hz) {
return Utils.formatFrequency(hz);
},
},
};
Tff7b72</script>

Tff7b72<style scopedTff7b72>
.interface-card {
@apply relative bg-white/95 dark:bg-zinc-900/85 backdrop-blur border border-gray-200 dark:border-zinc-800 rounded-3xl shadow-lg p-4 space-y-3 hover:z-10 min-w-0;
overflow: visible;
}
.interface-card__icon {
@apply w-12 h-12 rounded-2xl bg-blue-50 text-blue-600 dark:bg-blue-900/40 dark:text-blue-200 flex items-center justify-center;
}
.type-chip {
@apply inline-flex items-center rounded-full bg-gray-100 dark:bg-zinc-800 px-2 py-0.5 text-xs font-semibold text-gray-600 dark:text-gray-200;
}
.stat-chip {
@apply inline-flex items-center rounded-full border border-gray-200 dark:border-zinc-700 px-2 py-0.5;
}
.ifac-line {
@apply text-xs flex flex-wrap items-center gap-1;
}
.discoverable-chip {
@apply inline-flex items-center rounded-full bg-blue-100 text-blue-700 px-2 py-0.5 text-xs font-semibold dark:bg-blue-900/50 dark:text-blue-200;
}
.detail-grid {
@apply grid gap-3 sm:grid-cols-2;
}
.detail-label {
@apply text-xs uppercase tracking-wide text-gray-500 dark:text-gray-400;
}
.detail-value {
@apply text-sm font-medium text-gray-900 dark:text-white min-w-0 break-all;
}
Tff7b72</style>

Served by rngit 1.5.2 - Generated in 0.04s